import React, { useEffect, useRef } from 'react';
import './style.less';

// 生成随机整数
function generateRandom(min, max) {
  return ~~(Math.random() * (max - min)) + min;
}

// 基本画布类
class Base {
  constructor(id) {
    this.id = id;
    this.canvas = document.getElementById(id);
    this.context = this.canvas.getContext('2d');
  }
}

// 圆点类
class Point extends Base {
  constructor(id, dftX, dftY, radius = 3) {
    super(id);
    this.x = dftX || generateRandom(0, this.canvas.width - radius / 2);
    this.y = dftY || generateRandom(0, this.canvas.height - radius / 2);
    this.xSpeed = generateRandom(-100, 100);
    this.ySpeed = generateRandom(-100, 100);
    this.isStop = false;
    this.targetX = 0;
    this.targetY = 0;
    this.lastTime = null;
    this.radius = radius * devicePixelRatio;
  }

  draw() {
    if (this.lastTime) {
      const { isStop, targetX, targetY } = this;
      const duration = (Date.now() - this.lastTime) / 1000;
      let x = this.x + this.xSpeed * duration;
      let y = this.y + this.ySpeed * duration;
      if (isStop && targetX >= 0 && targetY >= 0) {
        if (this.x > targetX) {
          x = this.x - this.xSpeed * duration * 1.2;
        } else {
          x = this.x + this.xSpeed * duration * 1.2;
        }
        if (this.y > targetY) {
          y = this.y - this.ySpeed * duration * 1.2;
        } else {
          y = this.y + this.ySpeed * duration * 1.2;
        }
      }
      // 超出边界
      if (x > this.canvas.width - this.radius / 2) {
        x = this.canvas.width - this.radius / 2;
        this.xSpeed = -this.xSpeed;
      } else if (x < 0) {
        x = this.radius / 2;
        this.xSpeed = -this.xSpeed;
      }

      if (y > this.canvas.height - this.radius / 2) {
        y = this.canvas.height - this.radius / 2;
        this.ySpeed = -this.ySpeed;
      } else if (y < 0) {
        y = this.radius / 2;
        this.ySpeed = -this.ySpeed;
      }

      this.x = x;
      this.y = y;
    }

    this.context.beginPath();
    this.context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    this.context.fillStyle = 'white';
    this.context.fill();
    this.context.closePath();
    this.lastTime = Date.now();
  }

  getAxis() {
    return [this.x, this.y];
  }

  stop() {
    this.isStop = true;
    this.targetY = 0;
    this.targetX = 0;
  }

  restart() {
    this.isStop = false;
  }

  setTargetXY(x, y) {
    this.targetX = x;
    this.targetY = y;
  }
}

// 动画类
class Graph extends Base {
  constructor(id, maxDistance = 500, minDistance = 0) {
    super(id);
    this.maxDistance = maxDistance;
    this.minDistance = minDistance;
    this.points = [];
  }

  length = 25;

  init() {
    const { length } = this;
    const points = Array.from({ length }).map(() => {
      const p = new Point(this.id);
      p.draw();
      return p;
    });
    this.points = points;
    requestAnimationFrame(this.animateUpdate, {});
  }

  draw = () => {
    const { length, points } = this;
    for (let i = 0; i < length; i++) {
      const [x1, y1] = points[i].getAxis();
      for (let j = i + 1; j < points.length; j++) {
        const [x2, y2] = points[j].getAxis();
        const distance = Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
        if (distance <= this.maxDistance && distance >= this.minDistance) {
          this.context.beginPath();
          this.context.moveTo(x1, y1);
          this.context.lineTo(x2, y2);
          this.context.strokeStyle = `rgba(255, 255, 255, ${1 - distance / this.maxDistance})`;
          this.context.stroke();
          this.context.closePath();
        }
      }
    }
  };

  animateUpdate = () => {
    const { context, canvas, points, draw } = this;
    context.clearRect(0, 0, canvas.width, canvas.height);
    points.forEach(p => p.draw());
    draw();
    requestAnimationFrame(this.animateUpdate);
  };

  addPoint(p) {
    if (this.points.length > this.length + 5) {
      this.points.shift();
    }
    p.draw();
    this.points.push(p);
  }

  setSpeedWithZero() {
    this.points.forEach(p => {
      p.stop();
    });
  }

  restartSpeed() {
    this.points.forEach(p => p.restart());
  }

  setTargetXY(x, y) {
    this.points.forEach(p => p.setTargetXY(x, y));
  }
}

export default function Background() {
  const graph = useRef();
  // 画图
  const renderPoint = () => {
    const parent = document.querySelector('.background-canvas');
    const { width } = getComputedStyle(parent);
    const actuallWidth = width.replace('px', '');
    const canvas = document.createElement('canvas');
    canvas.id = 'canvas';
    canvas.width = actuallWidth * devicePixelRatio;
    canvas.height = 800 * devicePixelRatio;
    parent.appendChild(canvas);
    graph.current = new Graph('canvas', actuallWidth / 3.5);
    graph.current.init();

    // 注册鼠标交互事件
    registerEvents(canvas);
  };

  const registerEvents = canvas => {
    const mouseDotClick = e => {
      const { offsetX, offsetY } = e;
      graph.current.addPoint(new Point('canvas', offsetX, offsetY));
    };
    const mouseDotEnter = () => {
      graph.current.setSpeedWithZero();
    };
    const mouseDotMove = e => {
      const { offsetX, offsetY } = e;
      graph.current.setTargetXY(offsetX, offsetY);
    };
    const mouseDotLeave = () => {
      graph.current.restartSpeed();
    };
    canvas.addEventListener('click', mouseDotClick);
    canvas.addEventListener('mouseenter', mouseDotEnter);
    canvas.addEventListener('mousemove', mouseDotMove);
    canvas.addEventListener('mouseleave', mouseDotLeave);
  };

  useEffect(() => {
    renderPoint();
  }, []);
  return <div className="background-canvas"></div>;
}
